package project.batch;

import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.ThreadContext;

import project.common.master.AppConfig;
import project.common.master.Msg;
import batch.base.BaseBatch;
import batch.base.BatchParameter;
import core.config.Env;
import core.config.Factory;
import core.util.DateUtil;
import core.util.bean.Tuple;

/**
 * バッチ作成のための親抽象クラス
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public abstract class ProjectBatch extends BaseBatch {

	/** 処理件数設定用タグ：DBテーブル更新件数 */
	protected static final String UPDATE = "UPDATE";
	/** 処理件数設定用タグ：DBテーブル登録件数 */
	protected static final String INSERT = "INSERT";
	/** 処理件数設定用タグ：DBテーブル削除件数 */
	protected static final String DELETE = "DELETE";
	/** 処理件数設定用タグ：DBテーブル読込件数 */
	protected static final String SELECT = "SELECT";
	/** 処理件数設定用タグ：ファイル出力件数 */
	protected static final String WRITE = "WRITE";
	/** 処理件数設定用タグ：ファイル読込件数 */
	protected static final String READ = "READ";
	/** 処理件数設定用タグ：ファイル削除件数 */
	protected static final String REMOVE = "REMOVE";

	/** ログ出力用クラス */
	private static final Logger LOGGER = LogManager.getLogger(ProjectBatch.class);

	/** ログ出力区分 */
	private static final String KUBUN_RUN = "BatRun";
	/** ログサフィックス */
	private static final String LOG_SUFFIX = ".log";

	/** 環境種別 */
	private static final String KANKYO_SBT = "BATCH";
	/** 詳細コード 運用日付差分 */
	private static final String DETAIL_DATE = "DIFF_DATE";

	/** 機能ID */
	private String kinoId = null;
	/** バッチパラメタ */
	private ProjectParameter parameter = null;
	/** メッセージ汎用 */
	private final List<String> msgList = new ArrayList<>();

	/**
	 * バインドマップ化
	 * @param prms パラメタ
	 * @return バインドマップ
	 */
	protected final Map<String, Object> toBindMap(final Map<String, String[]> prms) {
		Map<String, Object> ret = new HashMap<String, Object>(prms);
		ret.put("Uid", getParameter().getUid());
		ret.put("DateTime", getParameter().getDateTime());
		return ret;
	}

	/**
	 * クエリインスタンス取得
	 * @param <T> ジェネリックス
	 * @param cls クエリクラス
	 * @return クエリインスタンス
	 */
	protected final <T> T create(final Class<T> cls) {
		T jq = Factory.create(cls);

		Method m = Factory.getMethod(cls, "setUid", String.class);
		Factory.invoke(jq, m, getParameter().getUid());

		m = Factory.getMethod(cls, "setDateTime", Timestamp.class);
		Factory.invoke(jq, m, getParameter().getDateTime());

		return jq;
	}

	/**
	 * バッチ実行ログヘッダ取得
	 *
	 * @return バッチ実行ログヘッダ
	 */
	protected final StringBuilder getLogHeader() {
		StringBuilder sb = new StringBuilder();
		sb.append(this.parameter.getUid());
		sb.append(",");
		sb.append(this.parameter.getIp());
		sb.append(",");
		sb.append(getKinoID());

		return sb;
	}

	/**
	 * バッチ実行ログヘッダ取得
	 *
	 * @param str 出力文字列
	 */
	protected final void writeBatchLog(final String str) {
		StringBuilder sb = getLogHeader();
		sb.append(",");
		sb.append(KUBUN_RUN);
		sb.append(",");
		sb.append(str);

		LOGGER.fatal(sb.toString());
	}

	/**
	 * 運用日付取得
	 *
	 * @return 運用日付(java.sql.Date型)
	 */
	protected final java.sql.Date getUnyoDate() {
		Date date = getParameter().getDateTime();

		AppConfig cfg = Factory.create(AppConfig.class);
		int diff = cfg.getValue(getKinoID(), KANKYO_SBT, DETAIL_DATE, 0);
		if (0 < diff) {
			date = DateUtil.calcDay(date, diff);
		}

		return DateUtil.toDate(date);
	}

	/**
	 * 機能ID取得
	 *
	 * @return 機能ID
	 */
	protected final String getKinoID() {
		if (this.kinoId == null) {
			String name = Factory.getClassBaseName(this.getClass());
			if (name.endsWith("Batch")) {
				name = name.substring(0, name.length() - "Batch".length());
			}
			return name;
		}
		return this.kinoId;
	}

	/**
	 * 機能ID設定
	 *
	 * @param id 機能ID
	 */
	protected final void setKinoID(final String id) {
		this.kinoId = id;
	}

	/**
	 * 処理件数設定を行う。
	 *
	 * @param name 処理対象名 (テーブル名やファイル名を指定)
	 * @param type UPDATE, INSERT, DELETE, SELECT, WRITE, READ のいずれか。
	 * @param count 処理件数
	 */
	protected final synchronized void setRecordCount(
			final String name, final String type, final int count) {
		if (name == null || type == null) {
			return;
		}

		String cnt = String.valueOf(count);
		Msg msg = Factory.create(Msg.class);
		if (UPDATE.equals(type)) {
			this.msgList.add(msg.getMessage(Env.getEnv(BatStatus.MSGID_UPD), name, cnt));
		} else if (INSERT.equals(type)) {
			this.msgList.add(msg.getMessage(Env.getEnv(BatStatus.MSGID_INS), name, cnt));
		} else if (DELETE.equals(type)) {
			this.msgList.add(msg.getMessage(Env.getEnv(BatStatus.MSGID_DEL), name, cnt));
		} else if (SELECT.equals(type)) {
			this.msgList.add(msg.getMessage(Env.getEnv(BatStatus.MSGID_SEL), name, cnt));
		} else if (WRITE.equals(type)) {
			this.msgList.add(msg.getMessage(Env.getEnv(BatStatus.MSGID_WRI), name, cnt));
		} else if (READ.equals(type)) {
			this.msgList.add(msg.getMessage(Env.getEnv(BatStatus.MSGID_RED), name, cnt));
		} else if (REMOVE.equals(type)) {
			this.msgList.add(msg.getMessage(Env.getEnv(BatStatus.MSGID_RMV), name, cnt));
		}
	}

	/**
	 * 処理件数印刷処理を行う。
	 *
	 */
	protected final void printResult() {
		for (final String msg : this.msgList) {
			LOGGER.fatal(msg);
		}

		List<Tuple<String>> list = getOutputFiles();
		if (list != null) {
			for (final Tuple<String> file : list) {
				if (0 < file.length()) {
					LOGGER.fatal(file.get(0));
				}
			}
		}
	}

	/**
	 * プロジェクトパラメタ取得
	 * @return プロジェクトパラメタ
	 */
	protected final ProjectParameter getParameter() {
		return this.parameter;
	}

	/**
	 * プロジェクトパラメタ
	 *
	 * @param prm プロジェクトパラメタ
	 */
	@Override
	public final void setBatchParameter(final BatchParameter prm) {
		if (ProjectParameter.class.isInstance(prm)) {
			this.parameter = ProjectParameter.class.cast(prm);
			this.parameter.setJobId(getKinoID());

			String name = getBatchLogFileName();
			String path = Env.getEnv("Batch.LogPath", "");

			ThreadContext.put("route", "Audit");
			ThreadContext.put("path", path);
			ThreadContext.put("filename", name);

			super.addFile(path + name, Env.getEnv("Batch.LogName"));
		}
	}

	/**
	 * バッチロガー情報取得
	 *
	 * @return バッチロガー情報
	 */
	private String getBatchLogFileName() {
		String dt = DateUtil.format(
				getParameter().getDateTime(), DateUtil.FORMAT_DATE_TIME);
		return getKinoID() + dt + this.parameter.getUid() + LOG_SUFFIX;
	}
}
